# coding=utf-8
from osgeo import gdal
from osgeo import ogr
from sqlalchemy import func
from controller.controller_base import ControllerBase
from common.write_log import writelog
from conf import constant
from dbs.models import *
import datetime, traceback
import zipfile, os, shutil,re
from flask import current_app
from conf.json_map import TEMP_DATA_EXTEND
from conf import constant, settings



GEO_COLUMN_NAME_STATUSFILED = 'status'  # 同步状态字段


# 同步状态标识
SYNC_STATUS_NO_CHANGE = 0   # 已同步
SYNC_STATUS_INSERT = 1  # 新增数据项
SYNC_STATUS_UPDATA = 2  # 修改数据项
SYNC_STATUS_DELETE = 3  # 删除数据项
SYNC_STATUS_ABNORMAL = 4  # goem异常
SYNC_STATUS_NONE = 5  # 值为空




class ExportController(ControllerBase):
    def __init__(self,extend_info=TEMP_DATA_EXTEND):
        super(ExportController, self).__init__()
        '''初始化驱动'''
        # 为了支持中文路径，请添加下面这句代码
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "NO")
        # 为了使属性表字段支持中文，请添加下面这句
        gdal.SetConfigOption("SHAPE_ENCODING", "")
        # 注册所有的驱动
        ogr.RegisterAll()
        # 数据格式的驱动
        self.driver = ogr.GetDriverByName('ESRI Shapefile')

        '''导出用属性'''
        self.record_id = None
        self.export_current_count = 0   # 当前导出了多少条数据
        self.export_data_count = 0  # 本次导处的数据总条数

        '''导入用属性'''
        self.extend_info = extend_info     # 任务扩展信息
        self.file_id = None     # 文件id
        self.upload_current_count = 0   # 当前导入了多少条数据
        self.upload_data_count = 0  # 本次导入的数据总条数
        self.sub_set_id = None  # 本次操作的归属数据子集id
        self.task_id = None    # 本次操作的task orm 对象

        '''导出映射'''
        self.layer_table_map = {  # 获取对应table
            'bridge': Bridge,
            'building': Building,
            'culvert': Culvert,
            'deceleration': Deceleration,
            'delineater': Delineater,
            'guardbar': GuardBar,
            'illumination': Illumination,
            'landuse': LandUse,
            'roadline': RoadLine,
            'roadpolygon': RoadPolygon,
            'roadsign': RoadSign,
            'treelawn': TreeLawn,
            'tunnel': Tunnel,
            'water': Water
        }
        self.geo_layer_map = {  # 字典表码 映射 图层名
            constant.GEO_LAYER_BRIDGE: 'bridge',
            constant.GEO_LAYER_BUILDING: 'building',
            constant.GEO_LAYER_CULVERT: 'culvert',
            constant.GEO_LAYER_DECELERATION: 'deceleration',
            constant.GEO_LAYER_DELINEATER: 'delineater',
            constant.GEO_LAYER_GUARDBAR: 'guardbar',
            constant.GEO_LAYER_ILLUMINATION: 'illumination',
            constant.GEO_LAYER_LANDUSE: 'landuse',
            constant.GEO_LAYER_ROADLINE: 'roadline',
            constant.GEO_LAYER_ROADPOLYGON: 'roadpolygon',
            constant.GEO_LAYER_ROADSIGN: 'roadsign',
            constant.GEO_LAYER_TREELAWN: 'treelawn',
            constant.GEO_LAYER_TUNNEL: 'tunnel',
            constant.GEO_LAYER_WATER: 'water'
        }
        self.shp_layer = {  # 对应的shape layer 操作对象
            'bridge': None,
            'building': None,
            'culvert': None,
            'deceleration': None,
            'delineater': None,
            'guardbar': None,
            'illumination': None,
            'landuse': None,
            'roadline': None,
            'roadpolygon': None,
            'roadsign': None,
            'treelawn': None,
            'tunnel': None,
            'water': None
        }

        '''导入映射'''
        self.entry_params_enum = [  # 允许导入postgis的字段  其中'geom'后面直接插入,不经过此处校验
            'global_id',
            'shape_length',
            'shape_area',
            'remark',
            'in_date',
            'va_date',
            'gis_class'
        ]
        self.shp_postgis_map = {  # gdb字段名 与 postgis字段名 的映射
            'geom': 'geom',
            'shape_length': 'shape_length',
            'shape_area': 'shape_area',
            'in_date': 'in_date',
            'va_date': 'va_date',
            'remark': 'remark',
            # 'code': 'code',
            # 'name': 'name',
            # 'bridge_code': 'code',
            # 'tunnel_code': 'code',
            # 'water_code': 'code',
            # 'road_name': 'name',
            # 'bridge_name': 'name',
            # 'building_name': 'name',
            # 'tunnel_name': 'name',
            # 'water_name': 'name',
            'gis_class': 'gis_class'
        }
        self.geometry_type_map = {  # 获取对应 GEO 数据类型
            'bridge': constant.GEO_TYPE_MULTIPOLYGON,
            'building': constant.GEO_TYPE_MULTIPOLYGON,
            'culvert': constant.GEO_TYPE_MULTIPOLYGON,
            'deceleration': constant.GEO_TYPE_MULTIPOLYGON,
            'delineater': constant.GEO_TYPE_MULTILINESTRING,
            'guardbar': constant.GEO_TYPE_MULTILINESTRING,
            'illumination': constant.GEO_TYPE_POINT,
            'landuse': constant.GEO_TYPE_MULTIPOLYGON,
            'roadline': constant.GEO_TYPE_MULTILINESTRING,
            'roadpolygon': constant.GEO_TYPE_MULTIPOLYGON,
            'roadsign': constant.GEO_TYPE_POINT,
            'treelawn': constant.GEO_TYPE_MULTIPOLYGON,
            'tunnel': constant.GEO_TYPE_MULTIPOLYGON,
            'water': constant.GEO_TYPE_MULTIPOLYGON
        }

    def open_shp(self, file_path, file_name):
        """打开shapefile文件"""
        dataset = self.driver.Open(file_path+file_name)
        if dataset is None:
            writelog('Could not open shapefile : %s' % (file_path+file_name))
            raise Exception
        return dataset

    def read_shp(self):
        pass

    def create_data_set(self, file_path, file_name):
        """创建保存所需的基础数据集对象"""
        def bing_common_defn(layer):
            """给图层绑定字段"""
            global_id_defn = ogr.FieldDefn('global_id', ogr.OFTString)
            global_id_defn.SetWidth(255)
            layer.CreateField(global_id_defn)

            gis_class_defn = ogr.FieldDefn('gis_class', ogr.OFTString)
            gis_class_defn.SetWidth(20)
            layer.CreateField(gis_class_defn)

            remark_defn = ogr.FieldDefn('remark', ogr.OFTString)
            remark_defn.SetWidth(255)
            layer.CreateField(remark_defn)

            shape_length_defn = ogr.FieldDefn('shape_len', ogr.OFTReal)
            layer.CreateField(shape_length_defn)

            shape_area = ogr.FieldDefn('shape_area', ogr.OFTReal)
            layer.CreateField(shape_area)

            # geo_layer_defn = ogr.FieldDefn('geo_layer', ogr.OFTInteger)
            # layer.CreateField(geo_layer_defn)

        if not os.path.exists(file_path):
            writelog(os.path.exists(file_path))
            os.makedirs(file_path)

        data_set = self.driver.CreateDataSource(os.path.join(file_path, file_name))  # 创建数据集
        #  创建图层
        self.shp_layer['bridge'] = data_set.CreateLayer('bridge', geom_type=ogr.wkbMultiPolygon)
        self.shp_layer['building'] = data_set.CreateLayer('building', geom_type=ogr.wkbMultiPolygon)
        self.shp_layer['culvert'] = data_set.CreateLayer('culvert', geom_type=ogr.wkbMultiPolygon)
        self.shp_layer['deceleration'] = data_set.CreateLayer('deceleration', geom_type=ogr.wkbMultiPolygon)
        self.shp_layer['delineater'] = data_set.CreateLayer('delineater', geom_type=ogr.wkbMultiLineString)
        self.shp_layer['guardbar'] = data_set.CreateLayer('guardbar', geom_type=ogr.wkbMultiLineString)
        self.shp_layer['illumination'] = data_set.CreateLayer('illumination', geom_type=ogr.wkbPoint)
        self.shp_layer['landuse'] = data_set.CreateLayer('landuse', geom_type=ogr.wkbMultiPolygon)
        self.shp_layer['roadline'] = data_set.CreateLayer('roadline', geom_type=ogr.wkbMultiLineString)
        self.shp_layer['roadpolygon'] = data_set.CreateLayer('roadpolygon', geom_type=ogr.wkbMultiPolygon)
        self.shp_layer['roadsign'] = data_set.CreateLayer('roadsign', geom_type=ogr.wkbPoint)
        self.shp_layer['treelawn'] = data_set.CreateLayer('treelawn', geom_type=ogr.wkbMultiPolygon)
        self.shp_layer['tunnel'] = data_set.CreateLayer('tunnel', geom_type=ogr.wkbMultiPolygon)
        self.shp_layer['water'] = data_set.CreateLayer('water', geom_type=ogr.wkbMultiPolygon)

        for layer in self.shp_layer.values():
            bing_common_defn(layer)   # 添加公用字段

        return data_set

    def export_layer(self, layer_name, datas):
        """导出每层数据"""
        def set_common_Field(shape_layer, table, feature, global_id, gis_class, remark, shape_length, shape_area):
            # 给行对象 添加公共属性 数据
            feature.SetField("global_id", global_id)
            feature.SetField('gis_class', gis_class)
            feature.SetField('remark', remark)
            feature.SetField('shape_len', shape_length)
            feature.SetField('shape_area', shape_area)
            gis = self.db.session.query(
                table.geom.ST_AsGeoJSON().label('geom')
            ).first()
            if gis:
                geom = ogr.CreateGeometryFromJson(gis.geom.encode('utf-8'))
                feature.SetGeometry(geom)
            else:
                raise Exception
            shape_layer.CreateFeature(feature)

        '''流程:遍历数据库实例list, 生成行对象,保存进图层对象中 '''
        shape_layer = self.shp_layer.get(layer_name)  # layer 取出操作对象
        table = self.layer_table_map.get(layer_name)  # orm table
        for data in datas:  # 临时表orm对象集合
            # 转换成字典数据
            data_dict = self.serialize(data)
            # 创建行对象
            defn = shape_layer.GetLayerDefn()   # 字段(列描述)集合对象
            feature = ogr.Feature(defn)     # 根据 列对象 生成行对象
            set_common_Field(shape_layer, table, feature, data_dict.get('global_id'), data_dict.get('gis_class'),
                             data_dict.get('remark'), data_dict.get('shape_length'), data_dict.get('shape_area'))
            feature.Destroy()   # 保存

            self.export_current_count += 1  # 导出数+1
            if self.export_current_count % 100 == 0:  # 每导出100条记录一次
                progress = round(self.export_current_count / (self.export_data_count * 1.0) * 100, 2)  # 总进度百分比,保留2位小数
                self.db.session.query(ExportRecord).filter(ExportRecord.record_id == self.record_id).update(
                    {"export_progress": progress})
                self.db.session.commit()


    def export_shp(self, sub_set_id, layer_codes, path, file_name, export_record):
        """导出为shp/zip文件"""
        def zip_dir(dirpath,outFullName):
            """
                压缩指定文件夹
                :param dirpath: 目标文件夹路径
                :param outFullName: 压缩文件保存路径+xxxx.zip
                :return: 无
                """
            zip = zipfile.ZipFile(outFullName, "w", zipfile.ZIP_DEFLATED)
            for path, dirnames, filenames in os.walk(dirpath):
                # 去掉目标跟路径，只对目标文件夹下边的文件及文件夹进行压缩
                fpath = path.replace(dirpath, '')
                fpath += file_name
                for filename in filenames:
                    zip.write(os.path.join(path, filename), os.path.join(fpath, filename))
            zip.close()

        def get_export_count(layer_codes):
            """
            获取本次导出数据总数
            :param layer_codes:导出哪些层
            :return: 总数
            """
            sum_count = 0
            for layer_code in layer_codes:
                layer_name = self.geo_layer_map.get(layer_code)
                table = self.layer_table_map.get(layer_name)
                count = self.db.session.query(func.count(table.sub_set_id)).filter(table.sub_set_id == sub_set_id).scalar()
                sum_count += count
            return sum_count

        self.record_id = export_record.record_id   # 导出记录表id
        self.export_data_count = get_export_count(layer_codes)  # 本次导出总数

        '''主流程'''
        try:
            for layer_code in layer_codes:
                layer_name = self.geo_layer_map.get(layer_code)  # 图层名
                layer_table = self.layer_table_map.get(layer_name)  # 图层对应orm对象
                shp_path = os.path.join(path, file_name)  # 单个shp文件夹的 路径为 '根目录/文件夹名/'
                data_set = self.create_data_set(shp_path, file_name)  # 创建基础数据集对象

                datas = self.db.session.query(layer_table).filter(  # orm结果对象list
                    layer_table.sub_set_id == sub_set_id
                ).all()
                writelog("sub_set_id: %s 开始对图层:%s进行导出操作" % (sub_set_id, layer_name))
                self.export_layer(layer_name, datas)
                writelog("sub_set_id: %s 图层:%s导出完成" % (sub_set_id, layer_name))

                data_set.Destroy()  # 保存文件(每层存一个)

            zip_dir(path + file_name, path + file_name+'.zip')  # 根目录另存了一个同名.zip文件
            self.db.session.query(ExportRecord).filter(ExportRecord.record_id == self.record_id).update(
                {"export_progress": 100})
            self.db.session.commit()
            shutil.rmtree(path+file_name)   # 移除原文件夹

        except:
            shutil.rmtree(path + file_name)
            raise

